package org.dq.like.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.dq.like.pojo.*;
import org.dq.like.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.*;

/**
 * 掘金自动点赞+评论
 *
 * @author klh
 * @date 2023/09/07 10:40:19
 */
@Slf4j
@RestController
@RequestMapping("/juejin")
public class JuejinLikeController {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private RedisUtil redisUtil;

    public static String juejin = "juejin";

    public static String aid = "2608";
    public static String uuid = "7249167827894486583";
    public static String spider = "0";

    /**
     * 掘金页面cookie(需要替换成自己的)
     */
    public static String cookie1 = "__tea_cookie_tokens_2608=%257B%2522web_id%2522%253A%25227249167827894486583%2522%252C%2522user_unique_id%2522%253A%25227249167827894486583%2522%252C%2522timestamp%2522%253A1687828421272%257D; n_mh=opaB0W0AMe8lPPeW6n5HW3l1IrdORSuWTKCUL4DNijo; store-region=cn-zj; store-region-src=uid; _tea_utm_cache_2608={%22utm_source%22:%22gold_browser_extension%22}; _tea_utm_cache_2018={%22utm_source%22:%22gold_browser_extension%22}; passport_csrf_token=c2a06dac50a76c069e3222989c6facc5; passport_csrf_token_default=c2a06dac50a76c069e3222989c6facc5; csrf_session_id=8fd2d258cc813e23f71804b79f27052f; sid_guard=425575116fb4247b9862f28676d7b2dc%7C1694761611%7C31536000%7CSat%2C+14-Sep-2024+07%3A06%3A51+GMT; uid_tt=9d51f70976f6629a263fc98123af4f5a; uid_tt_ss=9d51f70976f6629a263fc98123af4f5a; sid_tt=425575116fb4247b9862f28676d7b2dc; sessionid=425575116fb4247b9862f28676d7b2dc; sessionid_ss=425575116fb4247b9862f28676d7b2dc; sid_ucp_v1=1.0.0-KGI3MmQyM2ZiNzI0ODkzYzgyZTI3MDI1Yjc3ZjhlODIxMzBhNDAyMzcKFwjduKC2mI23AxCLhZCoBhiwFDgCQO8HGgJobCIgNDI1NTc1MTE2ZmI0MjQ3Yjk4NjJmMjg2NzZkN2IyZGM; ssid_ucp_v1=1.0.0-KGI3MmQyM2ZiNzI0ODkzYzgyZTI3MDI1Yjc3ZjhlODIxMzBhNDAyMzcKFwjduKC2mI23AxCLhZCoBhiwFDgCQO8HGgJobCIgNDI1NTc1MTE2ZmI0MjQ3Yjk4NjJmMjg2NzZkN2IyZGM; msToken=NhqUJcJT3E8ycFx2eB06SbK9k7UtgYj3GeosUbWCFDggCY8UCHBVfxtk4T-jzS-fD2TAidxBc8jwEUMJFQnEBeGhVALcedM_cKE7UzjBZHXOX3wF-1dwO1LlXGyOp6HtJg==";
    public static String cookie2 = "_tea_utm_cache_2018={%22utm_source%22:%22gold_browser_extension%22}; passport_csrf_token=c2a06dac50a76c069e3222989c6facc5; passport_csrf_token_default=c2a06dac50a76c069e3222989c6facc5; _tea_utm_cache_2608={%22utm_source%22:%22gold_browser_extension%22}; __tea_cookie_tokens_2608=%257B%2522web_id%2522%253A%25227278942506724689467%2522%252C%2522user_unique_id%2522%253A%25227278942506724689467%2522%252C%2522timestamp%2522%253A1694760884524%257D; csrf_session_id=8fd2d258cc813e23f71804b79f27052f; n_mh=kSEgXKS017HDdzt1rI5jXcf0OLgOpiVLH4hBOWGkq6A; passport_auth_status=8ac730867dc30b77af54138d8de242d2%2C; passport_auth_status_ss=8ac730867dc30b77af54138d8de242d2%2C; sid_guard=44b8e8d26abdc322154df83dca6263ae%7C1694760916%7C31536000%7CSat%2C+14-Sep-2024+06%3A55%3A16+GMT; uid_tt=4b68fc42f435c626faa8b8919ec9327f; uid_tt_ss=4b68fc42f435c626faa8b8919ec9327f; sid_tt=44b8e8d26abdc322154df83dca6263ae; sessionid=44b8e8d26abdc322154df83dca6263ae; sessionid_ss=44b8e8d26abdc322154df83dca6263ae; sid_ucp_v1=1.0.0-KDc2OGU0OTgxYTY5ZDc2OWU0M2EyYjlmMDMxMTI2ZDQ3MDkxNGFhODgKFgi07tDF-s0cENT_j6gGGLAUOAJA8QcaAmhsIiA0NGI4ZThkMjZhYmRjMzIyMTU0ZGY4M2RjYTYyNjNhZQ; ssid_ucp_v1=1.0.0-KDc2OGU0OTgxYTY5ZDc2OWU0M2EyYjlmMDMxMTI2ZDQ3MDkxNGFhODgKFgi07tDF-s0cENT_j6gGGLAUOAJA8QcaAmhsIiA0NGI4ZThkMjZhYmRjMzIyMTU0ZGY4M2RjYTYyNjNhZQ; store-region=cn-zj; store-region-src=uid; msToken=WtQBGO7Jy8WPlnJTLzzJY4x0-rp9ojrhnaEXBK8R9G7nlT8YyagF-UqsOnC6uFtGbSMLk815RBpNm578SmRdmV2QVs8zw16z39_mXFL94lYQLj409WBEEcByLZiPLQ==";

    /**
     * 自己的用户id(需要替换成自己的userId)
     */
    public static String myUserId = "1931195651202141";

    /**
     * ignoreDaysAgo天前的内容不点赞,忽略
     */
    private final int ignoreDaysAgo = 2;

    /**
     * 几天前的旧内容出现了oldTimeStop次之后认为没有新内容了,停止继续点赞
     */
    private int oldTimeStop = 10;

    /**
     * 已点过赞的旧内容出现了alreadyTimeStop次之后认为没有新内容了,停止继续点赞
     */
    private int alreadyTimeStop = 50;

    /**
     * 沸点列表接口
     */
    private static final String boilListUrl = "https://api.juejin.cn/recommend_api/v1/short_msg/recommend";
    /**
     * 某个人的沸点列表接口
     */
    private static final String someoneBoilListUrl = "https://api.juejin.cn/content_api/v1/short_msg/query_list";
    /**
     * 点赞接口
     */
    private static final String likeBoilUrl = "https://api.juejin.cn/interact_api/v1/digg/save";
    /**
     * 评论接口
     */
    private static final String reviewBoilUrl = "https://api.juejin.cn/interact_api/v1/comment/publish";
    /**
     * 文章列表接口
     */
    private static final String articleListUrl = "https://api.juejin.cn/content_api/v1/article/query_list";
    /**
     * 点赞文章接口
     */
    private static final String likeArticleUrl = "https://api.juejin.cn/interact_api/v1/digg/save";

    /**
     * 沸点列表
     * 自动点赞/评论
     *
     * @param isLike   是否点赞
     * @param isReview 是否评论
     * @return
     */
    @GetMapping("/likeBoil")
    public Result<?> likeBoil(@RequestParam(name = "isLike", defaultValue = "true") Boolean isLike,
                              @RequestParam(name = "isReview", defaultValue = "false") Boolean isReview) {
        log.info("自动列表点赞开始,干呀兄弟们!");
        String cursor = "0";
        Long daysAgoTime = getDaysAgoTime();
        String url = boilListUrl + "?" + getSuffix1();
        Boil reqParam = new Boil();
        HttpHeaders headers = new HttpHeaders();
        MediaType mediaType = MediaType.parseMediaType("application/json; charset=UTF-8");
        headers.setContentType(mediaType);
        List<String> cookies = new ArrayList<>();
        cookies.add(cookie1);
        headers.put(HttpHeaders.COOKIE, cookies);
        headers.setOrigin("chrome-extension://lecdifefmmfjnjjinhaennhdlmcaeeeb");
        int oldTime = 0;
        int alreadyTime = 0;
        for (int i = 0; i < 1000; i++) {
            if (oldTime > oldTimeStop) {
                break;
            }
            if (alreadyTime > alreadyTimeStop) {
                break;
            }
            reqParam = new Boil();
            reqParam.setCursor(cursor);
            headers = new HttpHeaders();
            mediaType = MediaType.parseMediaType("application/json; charset=UTF-8");
            headers.setContentType(mediaType);
            cookies = new ArrayList<>();
            cookies.add(cookie1);
            headers.put(HttpHeaders.COOKIE, cookies);
            headers.setOrigin("chrome-extension://lecdifefmmfjnjjinhaennhdlmcaeeeb");
            HttpEntity request = new HttpEntity<String>(JSON.toJSONString(reqParam), headers);
            ResponseEntity<String> resultEntity = restTemplate.postForEntity(url, request, String.class);
            if (resultEntity.hasBody()) {
                JSONObject result = JSONObject.parseObject(resultEntity.getBody());
                cursor = result.getString("cursor");
                JSONArray data = result.getJSONArray("data");
                if (data != null && !data.isEmpty()) {
                    boolean givedLike = false;
                    for (int j = 0; j < data.size(); j++) {
                        JSONObject jsonObject = data.getJSONObject(j);
                        JSONArray digg_users = jsonObject.getJSONArray("digg_user");
                        JSONObject msgInfo = jsonObject.getJSONObject("msg_Info");
                        Long mtime = msgInfo.getLong("mtime");
                        String content = msgInfo.getString("content");
                        if (oldTime > oldTimeStop) {
                            break;
                        }
                        if (alreadyTime > alreadyTimeStop) {
                            break;
                        }
                        if (mtime < daysAgoTime) {
                            log.info("忽略" + ignoreDaysAgo + "天前的沸点");
                            oldTime++;
                            continue;
                        }
                        String msgId = jsonObject.getString("msg_id");
                        //检查是否已经点过赞了
                        if (digg_users != null && digg_users.size() > 0) {
                            int count = 0;
                            for (int k = 0; k < digg_users.size(); k++) {
                                JSONObject jsonObject1 = digg_users.getJSONObject(k);
                                if (jsonObject1.getString("user_id").equals(myUserId)) {
                                    count++;
                                    break;
                                }
                            }
                            givedLike = count > 0;
                        }
                        if (!givedLike && !redisUtil.hHasKey(juejin + cookie1 + myUserId, msgId)) {
                            redisUtil.hset(juejin + cookie1 + myUserId, msgId, 1);
                            try {
                                //掘金限制1分钟只能请求20次接口,所以限制3秒钟请求一次
                                Thread.sleep(3000);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                            if (isLike) {
                                //点赞
                                likeBoil(msgId);
                            }
                            if (isReview || content.contains("点赞侠")) {
                                //评论,发现沸点内容带有点赞侠三个字的仍要评论
                                reviewBoil(msgId);
                            }
                        } else {
                            alreadyTime++;
                            log.info("点过赞了");
                        }
                    }
                } else {
                    break;
                }
            }
        }
        log.info("自动列表点赞完成,打完收工!");
        return Result.OK("自动列表点赞完成,打完收工!");
    }

    /**
     * 获取n天前的时间戳
     * 用于比较时间戳 n天前的内容不进行点赞和评论
     *
     * @return
     */
    private Long getDaysAgoTime() {
        Calendar ca = Calendar.getInstance();
        ca.setTime(new Date());
        ca.add(Calendar.DAY_OF_YEAR, -ignoreDaysAgo);
        return ca.getTimeInMillis() / 1000;
    }

    /**
     * 给某个人的全部沸点点赞
     *
     * @param user_id  用户id
     * @param isLike   是否点赞
     * @param isReview 是否评论
     * @return
     */
    @GetMapping("/likeBoilForSomeone")
    public Result<?> likeBoilForSomeone(@RequestParam(required = false, value = "user_id", defaultValue = "2845976976164215") String user_id,
                                        @RequestParam(name = "isLike", defaultValue = "true") Boolean isLike,
                                        @RequestParam(name = "isReview", defaultValue = "false") Boolean isReview) {
        log.info("给某个人所有沸点点赞开始,冲他丫的!");
        String url = someoneBoilListUrl + "?" + getSuffix2();
        String cursor = "0";
        SomeoneBoil reqParam = new SomeoneBoil(user_id);
        HttpHeaders headers = new HttpHeaders();
        MediaType mediaType = MediaType.parseMediaType("application/json; charset=UTF-8");
        headers.setContentType(mediaType);
        List<String> cookies = new ArrayList<>();
        cookies.add(cookie1);
        headers.put(HttpHeaders.COOKIE, cookies);
        headers.setOrigin("chrome-extension://lecdifefmmfjnjjinhaennhdlmcaeeeb");
        HttpEntity<String> request = new HttpEntity<String>(JSON.toJSONString(reqParam), headers);
        ResponseEntity<String> resultEntity = restTemplate.postForEntity(url, request, String.class);
        for (int i = 0; i < 1000; i++) {
            reqParam = new SomeoneBoil(user_id);
            reqParam.setCursor(cursor);
            headers = new HttpHeaders();
            mediaType = MediaType.parseMediaType("application/json; charset=UTF-8");
            headers.setContentType(mediaType);
            cookies = new ArrayList<>();
            cookies.add(cookie1);
            headers.put(HttpHeaders.COOKIE, cookies);
            headers.setOrigin("chrome-extension://lecdifefmmfjnjjinhaennhdlmcaeeeb");
            request = new HttpEntity<String>(JSON.toJSONString(reqParam), headers);
            resultEntity = restTemplate.postForEntity(url, request, String.class);
            if (resultEntity.hasBody()) {
                JSONObject result = JSONObject.parseObject(resultEntity.getBody());
                cursor = result.getString("cursor");
                JSONArray data = result.getJSONArray("data");
                if (data != null && !data.isEmpty()) {
                    boolean givedLike = false;
                    for (int j = 0; j < data.size(); j++) {
                        JSONObject jsonObject = data.getJSONObject(j);
                        JSONArray digg_users = jsonObject.getJSONArray("digg_user");
                        String msgId = jsonObject.getString("msg_id");
                        if (!digg_users.isEmpty()) {
                            int count = 0;
                            for (int k = 0; k < digg_users.size(); k++) {
                                JSONObject jsonObject1 = digg_users.getJSONObject(k);
                                if (jsonObject1.getString("user_id").equals(myUserId)) {
                                    count++;
                                    break;
                                }
                            }
                            givedLike = count > 0;
                        }
                        if (!givedLike && !redisUtil.hHasKey(juejin + cookie1 + myUserId, msgId)) {
                            redisUtil.hset(juejin + cookie1 + myUserId, msgId, 1);
                            try {
                                Thread.sleep(3000);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                            if (isLike) {
                                likeBoil(msgId);
                            }
                            if (isReview) {
                                reviewBoil(msgId);
                            }
                        } else {
                            log.info("点过赞了");
                        }
                    }
                } else {
                    break;
                }
            }
        }
        log.info("给某个人所有沸点点赞完成,舒服了!");
        return Result.OK("给某个人所有沸点点赞完成,舒服了!");
    }

    /**
     * 给某个人的全部文章点赞
     *
     * @param isLike
     * @param isReview
     * @param user_id
     * @return
     */
    @GetMapping("/likeArticleForSomeone")
    public Result<?> likeArticleForSomeone(@RequestParam(name = "isLike", defaultValue = "true") Boolean isLike,
                                           @RequestParam(name = "isReview", defaultValue = "false") Boolean isReview,
                                           @RequestParam(name = "user_id", defaultValue = "4178600045579725") String user_id) {
        log.info("给某个人的全部文章点赞开始,顶顶顶!");
        String url = articleListUrl + "?" + getSuffix2();
        String cursor = "0";
        SomeoneArticle reqParam = new SomeoneArticle(user_id);
        HttpHeaders headers = new HttpHeaders();
        MediaType mediaType = MediaType.parseMediaType("application/json; charset=UTF-8");
        headers.setContentType(mediaType);
        List<String> cookies = new ArrayList<>();
        cookies.add(cookie1);
        headers.put(HttpHeaders.COOKIE, cookies);
        headers.setOrigin("chrome-extension://lecdifefmmfjnjjinhaennhdlmcaeeeb");
        HttpEntity<String> request = new HttpEntity<String>(JSON.toJSONString(reqParam), headers);
        ResponseEntity<String> resultEntity = restTemplate.postForEntity(url, request, String.class);
        for (int i = 0; i < 1000; i++) {
            reqParam = new SomeoneArticle(user_id);
            reqParam.setCursor(cursor);
            headers = new HttpHeaders();
            mediaType = MediaType.parseMediaType("application/json; charset=UTF-8");
            headers.setContentType(mediaType);
            cookies = new ArrayList<>();
            cookies.add(cookie1);
            headers.put(HttpHeaders.COOKIE, cookies);
            headers.setOrigin("chrome-extension://lecdifefmmfjnjjinhaennhdlmcaeeeb");
            request = new HttpEntity<String>(JSON.toJSONString(reqParam), headers);
            resultEntity = restTemplate.postForEntity(url, request, String.class);
            if (resultEntity.hasBody()) {
                JSONObject result = JSONObject.parseObject(resultEntity.getBody());
                cursor = result.getString("cursor");
                JSONArray data = result.getJSONArray("data");
                if (data != null && !data.isEmpty()) {
                    boolean givedLike = false;
                    for (int j = 0; j < data.size(); j++) {
                        JSONObject jsonObject = data.getJSONObject(j);
                        JSONArray digg_users = jsonObject.getJSONArray("digg_user");
                        JSONObject articleInfo = jsonObject.getJSONObject("article_info");
                        String msgId = jsonObject.getString("article_id");
                        if (digg_users != null && !digg_users.isEmpty()) {
                            int count = 0;
                            for (int k = 0; k < digg_users.size(); k++) {
                                JSONObject jsonObject1 = digg_users.getJSONObject(k);
                                if (jsonObject1.getString("user_id").equals(myUserId)) {
                                    count++;
                                    break;
                                }
                            }
                            givedLike = count > 0;
                        }
                        if (!givedLike && !redisUtil.hHasKey(juejin + cookie1 + myUserId, msgId)) {
                            redisUtil.hset(juejin + cookie1 + myUserId, msgId, 1);
                            try {
                                Thread.sleep(3000);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                            if (isLike) {
                                likeArticle(msgId);
                            }
                            if (isReview) {
                                reviewBoil(msgId);
                            }
                        } else {
                            log.info("点过赞了");
                        }
                    }
                } else {
                    break;
                }
            }
        }
        log.info("给某个人的全部文章点赞完成");
        return Result.OK("给某个人的全部文章点赞完成");
    }

    /**
     * 给沸点点赞
     *
     * @param msgId 沸点id
     * @return
     */
    private boolean likeBoil(String msgId) {
        String url = likeBoilUrl + "?" + getSuffix1();
        LikeBoil reqParam = new LikeBoil(msgId);
        HttpHeaders headers = new HttpHeaders();
        MediaType mediaType = MediaType.parseMediaType("application/json; charset=UTF-8");
        headers.setContentType(mediaType);
        List<String> cookies = new ArrayList<>();
        cookies.add(cookie1);
        headers.put(HttpHeaders.COOKIE, cookies);
        headers.setOrigin("chrome-extension://lecdifefmmfjnjjinhaennhdlmcaeeeb");
        HttpEntity<String> request = new HttpEntity<String>(JSON.toJSONString(reqParam), headers);
        ResponseEntity<String> resultEntity = restTemplate.postForEntity(url, request, String.class);
        if (resultEntity.hasBody()) {
            log.info("点赞成功,msgId:{},接口返回：{}", msgId, resultEntity.getBody());
            JSONObject result = JSONObject.parseObject(resultEntity.getBody());
            if (null != result) {
                String errCode = result.getString("err_no");
                if (Objects.equals(errCode, "3001")) {
                    //已点过赞
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 给沸点评论
     *
     * @param msgId 沸点id
     * @return
     */
    private boolean reviewBoil(String msgId) {
        String url = reviewBoilUrl + "?" + getSuffix1();
        ReviewBoil reqParam = new ReviewBoil(msgId);
        HttpHeaders headers = new HttpHeaders();
        MediaType mediaType = MediaType.parseMediaType("application/json; charset=UTF-8");
        headers.setContentType(mediaType);
        List<String> cookies = new ArrayList<>();
        cookies.add(cookie1);
        headers.put(HttpHeaders.COOKIE, cookies);
        headers.setOrigin("chrome-extension://lecdifefmmfjnjjinhaennhdlmcaeeeb");
        HttpEntity<String> request = new HttpEntity<String>(JSON.toJSONString(reqParam), headers);
        ResponseEntity<String> resultEntity = restTemplate.postForEntity(url, request, String.class);
        if (resultEntity.hasBody()) {
            log.info("评论成功,msgId:{}", msgId);
            JSONObject result = JSONObject.parseObject(resultEntity.getBody());
            if (null != result) {
                String errCode = result.getString("err_no");
                if (Objects.equals(errCode, "3001")) {
                    //已点过赞
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 给文章点赞
     *
     * @param msgId 文章id
     * @return
     */
    private boolean likeArticle(String msgId) {
        String url = likeArticleUrl + "?" + getSuffix2() + "&verifyFp=f179d1b233021da9c677001fd1a0d16b054a49b949fa002783&fp=f179d1b233021da9c677001fd1a0d16b054a49b949fa002783&msToken=GF3L_CnlFMvFZqTlc4KzaNCnJATukzJYyKH5SyrVizSDhpaQXqurbvzIWOVvyq1Z4KeGe9a9Ltps0LOukQISedpls81d7uuBviHSIDOFgQWHQ3RAxsB5Qixrp0CJwSBd&a_bogus=d7MEgcZJMsm1r2kOR7Dz9Hm0H4j0YWRwgZEPY7Os60qL";
        LikeArticle reqParam = new LikeArticle(msgId);
        HttpHeaders headers = new HttpHeaders();
        MediaType mediaType = MediaType.parseMediaType("application/json; charset=UTF-8");
        headers.setContentType(mediaType);
        List<String> cookies = new ArrayList<>();
        cookies.add(cookie1);
        headers.put(HttpHeaders.COOKIE, cookies);
        headers.setOrigin("chrome-extension://lecdifefmmfjnjjinhaennhdlmcaeeeb");
        HttpEntity<String> request = new HttpEntity<String>(JSON.toJSONString(reqParam), headers);
        ResponseEntity<String> resultEntity = restTemplate.postForEntity(url, request, String.class);
        if (resultEntity.hasBody()) {
            log.info("点赞成功,msgId:{},接口返回：{}", msgId, resultEntity.getBody());
            JSONObject result = JSONObject.parseObject(resultEntity.getBody());
            if (null != result) {
                String errCode = result.getString("err_no");
                if (Objects.equals(errCode, "3001")) {
                    //已点过赞
                    return false;
                }
            }
        }
        return true;
    }

    @GetMapping("/setCookie")
    public Result<?> setCookie(@RequestParam("cookie") String cookie) {
        JuejinLikeController.cookie1 = cookie;
        return Result.ok("设置成功!");
    }

    @GetMapping("/setUserId")
    public Result<?> setUserId(@RequestParam("userId") String userId) {
        JuejinLikeController.myUserId = userId;
        return Result.ok("设置成功!");
    }

    /**
     * 获取第一种后缀
     * aid=2608&uuid=7249167827894486583
     */
    public static String getSuffix1() {
        return "aid=" + aid + "&uuid=" + uuid;
    }

    /**
     * 获取第二种后缀
     * aid=2608&uuid=7249167827894486583&spider=0
     */
    public static String getSuffix2() {
        return "aid=" + aid + "&uuid=" + uuid + "&spider=" + spider;
    }
}
